org.apache.commons.dbcp.datasources.InstanceKeyObjectFactory#removeInstance
method is not synchronized, so instanceMap field could be
accessed by multiple threads unsafely.
An exception expected
by running the test:
Exception in thread "Thread-0": java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
at java.util.HashMap$KeyIterator.next(HashMap.java:828)
at org.apache.commons.dbcp.datasources.InstanceKeyObjectFactory.registerNewInstance(InstanceKeyObjectFactory.java:50)
at org.apache.commons.dbcp.datasources.Dbcp369$1.run(Dbcp369.java:58)
at java.lang.Thread.run(Thread.java:662)
More details about this bug are at DBCP-369
JIRA page.
org.apache.commons.dbcp.datasources.InstanceKeyObjectFactory:
t1 t2 synchronized static String registerNewInstance(InstanceKeyDataSource ds) {
int max = 0;
Iterator i = instanceMap.keySet().iterator();
while (i.hasNext()) {
1 Object obj = i.next();
...
}
...
}
static void removeInstance(String key){
3 instanceMap.remove(key);
}
java.util.HashMap:
t1 t2 final Entry<K,V> nextEntry() {
2 if (modCount != expectedModCount)
5 throw new ConcurrentModificationException();
...
}
final Entry<K,V> removeEntryForKey(Object key) {
...
while (e != null) {
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
4 modCount++;
size--;
...
a) Thread t1 calls registerNewInstance method and comes to 1 to
call next.
b) Thread t1 comes to 2 in HashMap
class. Now modCount and expectedModCount are equal.
c) Before t1 executes 2, context switched, and thread t2 calls remove
method at 3.
d) When t2 calls remove method, it comes
to removeEntryForKey method in HashMap class, and
executes 4.
e) Context switched, t1 now checks modCount
and expectedModCount at 2. They are not equal after step d.
f) T1 comes to 5 and throws exception.
This bug is reproduced under dbcp 1.2 and JDK 1.6_33.
Execute the following scripts to run the test to reproduce the bug (assume the location of the dbcp test project is dbcp_test_home).
The default argument values will be taken if not specified.For
example,
${dbcp_test_home}/scripts/369.sh
is
the same as
${dbcp_test_home}/scripts/369.sh --loops
10
| Option | Function | Default Value | Valid Values |
| --loops,-l | number of loops. | 10 | integer |